package com.starview.star.operators.system.runtime;

import java.util.Map.Entry;
import java.util.Properties;

import com.starview.platform.data.EvaluationException;
import com.starview.platform.data.IFunction;
import com.starview.platform.data.IMap;
import com.starview.platform.data.IValue;
import com.starview.platform.data.type.IType;
import com.starview.platform.data.type.StandardTypes;
import com.starview.platform.data.type.TypeUtils;
import com.starview.platform.data.value.Factory;
import com.starview.star.operators.CafeEnter;

public class SystemUtils
{
  private static final IType stringType = StandardTypes.stringType;
  private static final IType integerType = StandardTypes.integerType;

  public static class Exit implements IFunction
  {
    @Override
    public IType getType()
    {
      return TypeUtils.procedureType(integerType);
    }

    @CafeEnter
    public static IValue enter(int code)
    {
      System.exit(code);
      return StandardTypes.unit;
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException
    {
      return enter(Factory.intValue(args[0]));
    }
  }

  public static class GetProperty implements IFunction
  {
    @Override
    public IType getType()
    {
      return TypeUtils.functionType(stringType, stringType, stringType);
    }

    @CafeEnter
    public static IValue enter(IValue key, IValue deflt) throws EvaluationException
    {
      return Factory.newString(System.getProperty(Factory.stringValue(key), Factory.stringValue(deflt)));
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException
    {
      return enter(args[0], args[1]);
    }
  }

  public static class SetProperty implements IFunction
  {
    @Override
    public IType getType()
    {
      return TypeUtils.procedureType(stringType, stringType);
    }

    @CafeEnter
    public static IValue enter(IValue key, IValue value) throws EvaluationException
    {
      System.setProperty(Factory.stringValue(key), Factory.stringValue(value));
      return StandardTypes.unit;
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException
    {
      return enter(args[0], args[1]);
    }
  }

  public static class ClearProperty implements IFunction
  {
    @Override
    public IType getType()
    {
      return TypeUtils.procedureType(stringType);
    }

    @CafeEnter
    public static IValue enter(IValue key) throws EvaluationException
    {
      System.clearProperty(Factory.stringValue(key));
      return StandardTypes.unit;
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException
    {
      return enter(args[0]);
    }
  }

  public static class GetProperties implements IFunction
  {

    @Override
    public IType getType()
    {
      return TypeUtils.functionType(TypeUtils.hashType(stringType, stringType));
    }

    @CafeEnter
    public static IValue enter() throws EvaluationException
    {
      Properties appProperties = System.getProperties();
      IMap encapsulated = Factory.newMap(stringType, stringType);

      for (Entry<Object, Object> entry : appProperties.entrySet()) {
        Object key = entry.getKey();
        Object val = entry.getValue();

        if (key instanceof String && val instanceof String)
          encapsulated = encapsulated.setMember(Factory.newString((String) key), Factory.newString((String) val));
      }
      return encapsulated;
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException
    {
      enter();
      return null;
    }
  }

  public static class GetEnv implements IFunction
  {
    @Override
    public IType getType()
    {
      return TypeUtils.functionType(stringType, stringType, stringType);
    }

    @CafeEnter
    public static IValue enter(IValue name, IValue deflt) throws EvaluationException
    {
      String envVar = System.getenv(Factory.stringValue(name));
      if (envVar == null)
        return deflt;
      return Factory.newString(envVar);
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException
    {
      return enter(args[0], args[1]);
    }
  }

  public static class Exec implements IFunction
  {
    @Override
    public IType getType()
    {
      return TypeUtils.functionType(stringType, TypeUtils.hashType(stringType, stringType), StandardTypes.integerType);
    }

    @CafeEnter
    public static IValue enter(IValue cmd, IMap env) throws EvaluationException
    {
      String envp[] = null;

      if (!env.isEmpty()) {
        envp = new String[env.size()];
        int ix = 0;
        for (Entry<IValue, IValue> entry : env) {
          String key = Factory.stringValue(entry.getKey());
          String val = Factory.stringValue(entry.getValue());
          envp[ix++] = key + "=" + val;
        }
      }

      Runtime runTime = Runtime.getRuntime();
      try {
        return Factory.newInt(runTime.exec(Factory.stringValue(cmd), envp).waitFor());
      } catch (Exception e) {
        throw new EvaluationException(e.getMessage());
      }
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException
    {
      return enter(args[0], (IMap) args[1]);
    }
  }
}
